package czk.concrent.safepoint;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 安全点测试
 * 安全点位置的选取基本上是以“是否具有让程序长时间执行的特征”为标准进行选定的。
 * HotSpot会在所有方法的临返回之前，以及所有非counted loop的循环的回跳之前放置安全点。
 * “长时间执行”的最明显特征就是指令序列的复用，例如方法调用、循环跳转、异常跳转等都属于指令序列复用，所以只有具有这些功能的指令才会产生安全点。
 * 而为什么把这些位置设置为jvm的安全点呢？
 * 主要目的就是避免程序长时间无法进入 safepoint ,比如 JVM 在做 GC 之前要等所有的应用线程进入到安全点后 VM 线程才能分派 GC 任务。
 * 如果有线程一直没有进入到安全点,就会导致 GC 时 JVM 停顿时间延长。
 *
 * @Author:ChenZhangKun
 * @Date: 2021/4/15 10:20
 */
public class SafePointTest {
    public static void main(String[] args) throws InterruptedException {
        SafePointTest safePointTest = new SafePointTest();
        safePointTest.fun_1();
        // safePointTest.fun_2();
        //safePointTest.fun_3();
    }
    /**
     * 通常，HotSpot JVM 会在循环中添加一个safepoint 轮询，以便在 JVM 需要执行 stop the world 操作时暂停线程。
     * safepoint 轮询不是免费的（也就是说，它有一些性能开销）。
     * 因此 JIT 编译器会在可能的情况下尝试消除它。
     * 其中一个优化是从 counted loops中删除 safepoint 轮询。
     * 那么什么是 counted loops 呢？
     * 你程序里面的 for（int i=0；i<1000000000；i++ 是一个典型的 counted loops：
     * 就是有明确的循环计数器变量，而且该变量有明确的起始值、终止值、步进长度的循环
     * 当我们使用 JDK8 JIT 编译这样的循环时没有 safepoint 轮询。
     * 但是，这是一个非常长的循环，长到需要几秒钟才能完成，所以当这个循环运行时，JVM将无法停止线程。
     * HotSpot JVM不仅在 GC 的时候使用 safepoints，还有许多其他操作也使用 safepoints。
     * 特别是当有清理任务要做时，它会周期性地停止 Java 线程。
     * 周期由 -XX:GuaranteedSafepointInterval 选项控制，该选项默认为 1000ms
     */

    /**
     * 奇妙的事情发生了，按照代码来看，主线程休眠 1000ms 后就会输出结果，
     * 但是实际情况确实主线程一直等待 t1,t2 执行结束才继续执行。
     * 原因：
     * 1.你启动了两个长的、不间断的循环（内部没有安全点检查）。
     * 2.主线程进入睡眠状态 1 秒钟。
     * 3.在1000 ms（GuaranteedSafepointInterval）之后，JVM尝试在安全点停止，以便Java线程进行定期清理，但是直到计数循环完成后才能执行此操作。
     * 5.Thread.sleep 方法从 native 返回，发现安全点操作正在进行中，于是把自己挂起，直到操作结束。
     * 优化：
     * 1 -XX:+UseCountedLoopSafepoints 选项可以关闭安全点轮询的优化。
     * 2 将 int i 更改为 long i ，循环将不再被视为 counted loop
     *
     * @throws InterruptedException
     */
    public void fun_1() throws InterruptedException {
        // 定义原子变量
        AtomicInteger num = new AtomicInteger(0);
        Runnable runnable = () -> {
            for (int i = 0; i < 1000000000; i++) {
                num.getAndAdd(1);
            }
        };
        // 启动线程
        new Thread(runnable).start();
        new Thread(runnable).start();
        // 主线程休眠
        TimeUnit.SECONDS.sleep(1);
        // 输出
        System.out.println("num=" + num);
    }

    /**
     * 普通int类型
     */
    int num = 0;

    /**
     * 此时主线程不会挂起
     *
     * @throws InterruptedException
     */
    public void fun_2() throws InterruptedException {
        Runnable runnable = () -> {
            for (int i = 0; i < 1000000000; i++) {
                num++;
            }
        };
        // 启动线程
        new Thread(runnable).start();
        new Thread(runnable).start();
        // 主线程休眠
        TimeUnit.SECONDS.sleep(1);
        // 输出
        System.out.println("num=" + num);
    }

    /**
     * 定义可见性变量
     */
    volatile int num1 = 0;

    /**
     * 此时主线程又会被挂起
     *
     * @throws InterruptedException
     */
    public void fun_3() throws InterruptedException {
        Runnable runnable = () -> {
            for (int i = 0; i < 1000000000; i++) {
                num1++;
            }
        };
        // 启动线程
        new Thread(runnable).start();
        new Thread(runnable).start();
        // 主线程休眠
        TimeUnit.SECONDS.sleep(1);
        // 输出
        System.out.println("num=" + num1);
    }
}
